package pers.vic.test.rpc.framework;

import java.io.IOException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.Remote;
import java.util.List;

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.data.Stat;

/**
 * 
 * 注册器工具； 通过zk连接对象 和传入的Remote接口实现对象，完成RMI地址的拼接，和保存（保存在zk） <br/>
 * TODO 缺少locateRegistry对象。 缺少当前类型中属性的赋值过程。整体流程，缺少ZkConnection的创建过程
 * 
 * @author Vic.xu
 * @date 2021/08/09
 */
public class RpcRegistry {

    private ZkConnection zkConnection;

    private String ip;

    private int port;

    /**
     * 注册服务的方法 <br/>
     * 1. 拼接RMI的访问地址URI <br/>
     * 2. 把访问地址URI存储在ZK (节点存在， 则删除)<br/>
     * 
     * 
     * @param serviceInterface
     *            服务接口对象， 接口必须Remote的子接口
     * @param serviceObject
     *            服务类实现类型的对象，必须实现serviceInterface， 且是Remote接口直接或间接实现类
     * @throws IOException
     * @throws InterruptedException
     * @throws KeeperException
     *             注册失败
     */
    public void regstryService(Class<? extends Remote> serviceInterface, Remote serviceObject)
        throws KeeperException, InterruptedException, IOException {
        // rmi://ip:port/com.pers....xx.service
        String rmi = "rmi://" + ip + ":" + port + "/" + serviceInterface.getName();
        // 拼接一个有规则的zk存储节点命名
        String path = "/vic/rpc/" + serviceInterface.getName();
        List<String> children = zkConnection.getConnction().getChildren("/vic/rpc", false);
        System.out.println("children:" + String.join("  ", children));
        if (children.contains(serviceInterface.getName())) {
            Stat stat = new Stat();
            zkConnection.getConnction().getData(path, false, stat);
            zkConnection.getConnction().delete(path, stat.getCversion());
        }
        zkConnection.getConnction().create(path, rmi.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        // LocateRegistry.getRegistry(port);
        // 把服务对象， 在RMI的Registry中注册
        Naming.rebind(rmi, serviceObject);
    }

    /**
     * 根据服务接口类型， 访问ZK，获取RMI的远程代理对象 <br/>
     * 1. 拼接一个zk中的节点名称 <br/>
     * 2. 访问zk， 查询节点中存储的数据 <br/>
     * 3. 根据查询的结构，创建一个代理对象
     * 
     * @return
     * @throws IOException
     * @throws InterruptedException
     * @throws KeeperException
     * @throws NotBoundException
     */
    @SuppressWarnings("unchecked")
    public <T extends Remote> T getServiceProxy(Class<? extends Remote> serviceInterface)
        throws KeeperException, InterruptedException, IOException, NotBoundException {
        String path = "/vic/rpc/" + serviceInterface.getName();
        byte[] datas = zkConnection.getConnction().getData(path, false, null);
        // rmi访问地址
        String rmi = new String(datas);
        // 创建代理对象
        Remote lookup = Naming.lookup(rmi);
        return (T)lookup;
    }

    /**
     * @return the zkConnection
     */
    public ZkConnection getZkConnection() {
        return zkConnection;
    }

    /**
     * @param zkConnection
     *            the zkConnection to set
     */
    public void setZkConnection(ZkConnection zkConnection) {
        this.zkConnection = zkConnection;
    }

    /**
     * @return the ip
     */
    public String getIp() {
        return ip;
    }

    /**
     * @param ip
     *            the ip to set
     */
    public void setIp(String ip) {
        this.ip = ip;
    }

    /**
     * @return the port
     */
    public int getPort() {
        return port;
    }

    /**
     * @param port
     *            the port to set
     */
    public void setPort(int port) {
        this.port = port;
    }

}
